接下來要介紹的實例,是員工資料清單表格<EmployeeTable />
,其中涉及到跨組件(component)的資料傳遞,即props(父傳子)及emits(子傳父),先參考以下程式碼:
<!-- src/views/Employee.vue -->
<template>
<section id="member-settings">
<h1>成員設定</h1>
<!-- 員工資料清單表格 -->
<EmployeeTable
:employees="employees"
@edit="openEdit"
@delete="confirmDelete"
:current-page="currentPage"
:page-size="pageSize"
/>
</section>
</template>
我們可以看到:employees=”employees”
的語法,左邊的:employees
,代表<EmployeeTable />
元件的props名稱,右邊的”employees”
,代表父組件的data
或computed
變數,乃用於傳送資料給子組件。
至於@edit=”openEdit”
的語法,左邊的@edit
,代表emits名稱為edit的事件,而該事件乃來自於子組件,並受到父組件監聽,右邊的”openEdit”
,代表父組件的方法,於接收子組件的事件與參數後,以此進行函式運算。
簡單彙總props及emits語法,參考如下:
<!-- 父組件把資料傳給子組件的props -->
:propName="父組件資料"
<!-- 子組件用$emit('eventName', payload)發事件給父組件 -->
@eventName="父組件方法"
至於若要確認父、子組件之間的資料傳遞與事件連結方式,要參考父組件區塊中props傳遞與emit事件的設定,即:
// src/views/Employee.vue
import store from "@/store";
import { mapGetters } from "vuex";
// 註冊EmployeeTable子組件,父組件透過props把資料傳給它,並監聽它的emits(事件)
import EmployeeTable from "@/components/Employee/EmployeeTable.vue";
export default {
name: "EmployeePage",
components: {
EmployeeTable,
},
// 接收Vuex狀態(state/getters),部分資料以props傳遞給子組件EmployeeTable.vue
computed: {
// Vuex getters(唯讀)
...mapGetters("employee", [
"pageSize",
"employees",
]),
// 採用getter/setter形式,同時也可支援雙向綁定,如v-model="currentPage"
currentPage: {
// getter
get() {
return this.$store.state.employee.currentPage;
},
// setter
set(newPage) {
this.$store.commit(`employee/${SET_CURRENT_PAGE}`, newPage);
}
}
},
methods: {
// 接收來自子組件EmployeeTable.vue的@edit事件(emits)
openEdit(emp) {
this.isEdit = true;
this.editingEmployee = emp;
this.showModal = true;
},
// 接收來自子組件EmployeeTable.vue的@delete事件(emits)
confirmDelete(emp) {
if (!confirm(`確定刪除 ${emp.name}?`)) return;
this.$store.dispatch(`employee/${DELETE_EMPLOYEE}`, emp.id)
.then(() => {
this.refreshList();
});
},
},
};
src/components/Employee
的子組件EmployeeTable.vue
,並於父組件的components
中註冊,以便於<template />
中直接使用<EmployeeTable />
元件。<EmployeeTable />
中共有三個props:
employees
及page-size
,乃經Vuex getters讀取狀態後,藉由computed
變數employees
及pageSize
分別取得資料,再傳給子組件current-page
,則經由Vuex getter讀取狀態或setter更新狀態後,藉由computed
變數currentPage
取得資料後,再傳給子組件。<EmployeeTable />
中共有二個emits,則分別監聽來自子組件的edit
及delete
事件,接收emp
參數後,分別執行函式openEdit(emp)
及confirmDelete(emp)
,分別為開啟編輯模式modal及刪除員工資料。進一步看子組件EmployeeTable.vue
,如:
<!-- src/components/Employee/EmployeeTable.vue -->
<template>
<b-table
:items="items"
:fields="fields"
>
<!-- 序號欄:顯示計算後的index -->
<template #cell(index)="row">
{{ row.item.index }}
</template>
<!-- 操作欄:三點選單 -->
<template #cell(actions)="row">
<b-dropdown right size="sm" variant="link" no-caret>
<template #button-content>
<i class="fas fa-ellipsis-v"></i>
</template>
<!-- 點擊後通知父組件,並傳回該列資料row.item -->
<b-dropdown-item @click="$emit('edit', row.item)">編輯</b-dropdown-item>
<b-dropdown-item @click="$emit('delete', row.item)">刪除</b-dropdown-item>
</b-dropdown>
</template>
</b-table>
</template>
// src/components/Employee/EmployeeTable.vue
export default {
name: "EmployeeTable",
// 父組件透過props傳入資料(employees, currentPage, pageSize),供表格顯示與計算
props: {
employees: { type: Array, required: true },
currentPage: { type: Number, required: true },
pageSize: { type: Number, required: true }
},
// 回傳給父組件的事件(edit, delete),並傳回選取列的資料(row.item)
emits: ["edit", "delete"],
computed: {
// 將父組件傳入的employees轉換成表格顯示用items,以顯示資料畫面
items() {
return this.employees.map((emp, idx) => ({
...emp,
index: (this.currentPage - 1) * this.pageSize + idx + 1
}));
}
}
};
<b-table />
,相關的使用方式,可參考官方網站文件computed
屬性中,將employees
、currentPage
、pageSize
進行運算,產成items()
,並綁定至<b-table />
的:items
屬性,以渲染表格內容並呈現員工資料。edit
、delete
)後,再透過emit事件回傳該列資料row.item
給父組件使用。到此,我們總算完成跨組件props及emits的介紹,藉由區分各組件進行撰寫,可達到不同功能集中在對應組件內管理的作用,除了方便日後程式修改,也適於可解讀性,這一切都要歸功於父、子組件傳遞資料的強大功能。